const tool = require('./common');

class validate {
    /**
     * 自定义验证类型
     * @type {{}}
     * @private
     */
    _type = {};
    /**
     * 当前验证规则
     * @type {{}}
     * @private
     */
    _rules = {};
    /**
     * 验证类型别名
     * @type {{"<=": string, same: string, "<": string, "=": string, ">": string, ">=": string}}
     * @private
     */
    _alias = {'>': 'gt', '>=': 'egt', '<': 'lt', '<=': 'elt', '=': 'eq', 'same': 'eq'};
    /**
     * 验证失败错误信息
     * @private
     */
    _error;

    rules(rules = {}) {
        this._rules = rules;
        return this;
    }

    check(data = {}) {
        let title = '', val, rule, rules = this._rules, result = false;
        for (let ri in rules) {console.log('ri->',data,data[ri]);
            if (/|/i.test(ri)) {
                [ri, title] = ri.split('|');
            } else title = ri;
            val = ri in data ? data[ri] : '';
            rule = rules[ri];

            //是否是方法
            if (tool.isFunction(rule)) {
                result = rule.call();
            } else {
                result = this._checkItem(ri, val, rule, data, title);
            }

            if (true !== result) {
                // 没有返回true 则表示验证失败
                /* if (!empty($this->batch)) {
                     // 批量验证
                     $this->error[$key] = $result;
                 } else if ($this->failException) {
                     throw new ValidateException($result);
                 } else {*/
                this._error = result;
                return false;
                /*}*/
            }
        }

        if (this._error) {
            /*if ($this->failException) {
                throw new ValidateException($this->error);
            }*/
            return false;
        }

        return true;
    }

    _checkItem(field, fieldVal, ruleArr, data, title, msg = []) {
        console.log('ruleArr:', ruleArr);
        let rules = [], result = true, type, info, message;
        if (tool.isString(ruleArr)) {
            rules = ruleArr.split('|');
        } else rules = ruleArr;

        //无规则，通过
        if (rules.length <= 0) return result;

        let i = 0;
        for (let key in rules) {
            let rule = rules[key];
            if (tool.isFunction(rule)) {
                result = item.apply([fieldVal, data]);
            } else {
                // 判断验证类型
                [type, rule, info] = this._getValidateType(key, rule);
                console.log('validType:', fieldVal, type, rule, info);
                /* if (isset($this->append[$field]) && in_array($info, $this->append[$field])) {
                 } else if (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) {
                     // 规则已经移除
                     $i++;
                     continue;
                 }*/

                if (type in this._type) {
                    result = this._type[type].apply(this, [fieldVal, rule, data, field, title]);
                } else if ('must' == info || /^require/i.test(info) || (fieldVal !== undefined && fieldVal.length)) {
                    if (!this.__proto__.hasOwnProperty(type)) tool.throwError(type + ' 方法不存在');
                    result = this[type].apply(this, [fieldVal, rule, data, field, title]);
                } else {
                    result = true;
                }
            }

            if (false === result) {
                // 验证失败 返回错误信息
                if (msg && msg[i]) {
                    message = msg[i];
                    /*if (is_string($message) && strpos($message, '{%') === 0) {
                        message = $this->lang->get(substr($message, 2, -1));
                    }*/
                } else {
                    info == 'require' && (message = `${field} 必填`);
                    info == 'confirm' && (message = `${field}与${rule} 不一致`);
                    message || (message = field + '：' + rule + ',' + info + ',' + fieldVal + ',' + title);//$this->getRuleMsg($field, $title, $info, $rule);
                }

                return message;
            } else if (true !== result) {
                // 返回自定义错误信息
                if (tool.isString(result) && /:/i.test(result)) {
                    result = result.replace(':attribute', title);
                    /*if (strpos($result, ':rule') && is_scalar($rule)) {
                        result = str_replace(':rule', (string)$rule, $result);
                    }*/
                }

                return result;
            }
            i++;
        }

        return result;
    }

    /**
     * 获取验证类型
     * @param key
     * @param rule
     * @returns {*[]}
     * @private
     */
    _getValidateType(key, rule) {
        let type, info;
        // 判断验证类型
        /*if (!i($key)) {
            if (isset($this->alias[$key])) {
                // 判断别名
                $key = $this->alias[$key];
            }
            return [$key, $rule, $key];
        }*/

        if (/:/i.test(rule)) {
            [type, rule] = rule.split(':', 2);
            // 判断别名
            type in this._alias && (type = this._alias[type]);
            info = type;
        } else if (this.__proto__.hasOwnProperty(rule)) {
            type = rule;
            info = rule;
            rule = '';
        } else {
            type = 'is';
            info = rule;
        }

        return [type, rule, info];
    }

    /**
     * 获取错误信息
     * @returns {*}
     */
    getError() {
        return this._error;
    }

    /**
     * 验证是否和某个字段的值一致
     * @access public
     * @param mixed  $value 字段值
     * @param mixed  $rule  验证规则
     * @param array  $data  数据
     * @param string $field 字段名
     * @return bool
     */
    confirm(fieldVal, rule, data = {}, field = '') {
        if ('' == rule) {
            if (field.indexOf('_confirm')) {
                rule = field.slice(0, field.indexOf('_confirm'));
            } else {
                rule = field + '_confirm';
            }
        }

        return this._getDataValue(data, rule) === fieldVal;
    }

    /**
     * 验证是否和某个字段的值是否不同
     * @access public
     * @param mixed $fieldVal 字段值
     * @param mixed $rule  验证规则
     * @param array $data  数据
     * @return bool
     */
    different(fieldVal, rule, data = {}) {
        return this._getDataValue(data, rule) != fieldVal;
    }

    /**
     * 验证是否大于等于某个值
     * @access public
     * @param mixed $value 字段值
     * @param mixed $rule  验证规则
     * @param array $data  数据
     * @return bool
     */
    egt(fieldVal, rule, data = {}) {
        return fieldVal >= this._getDataValue(data, rule);
    }

    /**
     * 验证是否大于某个值
     * @access public
     * @param mixed $value 字段值
     * @param mixed $rule  验证规则
     * @param array $data  数据
     * @return bool
     */
    gt(fieldVal, rule, data = {}) {
        return fieldVal > this._getDataValue(data, rule);
    }

    /**
     * 验证是否小于等于某个值
     * @access public
     * @param mixed $value 字段值
     * @param mixed $rule  验证规则
     * @param array $data  数据
     * @return bool
     */
    elt(fieldVal, rule, data = {}) {
        return fieldVal <= this._getDataValue(data, rule);
    }

    /**
     * 验证是否小于某个值
     * @access public
     * @param mixed $value 字段值
     * @param mixed $rule  验证规则
     * @param array $data  数据
     * @return bool
     */
    lt(fieldVal, rule, data = {}) {
        return fieldVal < this._getDataValue(data, rule);
    }

    /**
     * 验证是否等于某个值
     * @access public
     * @param mixed $value 字段值
     * @param mixed $rule  验证规则
     * @return bool
     */
    eq(fieldVal, rule) {
        return fieldVal == rule;
    }

    /**
     * 必须验证
     * @param $value 字段值
     * @param $rule 验证规则
     * @returns {boolean}
     */
    must(fieldVal, rule = null) {
        return !empty(fieldVal) || '0' == value;
    }

    is(fieldVal, rule, data) {
        let result = false;
        switch (rule) {
            case "require":
                result = /[^(^\s*)|(\s*$)]/.test(fieldVal);
                break;
            case "number":
                result = /^\d+$/.test(fieldVal);
                break;
            case "integer":
                result = /^[-\+]?\d+$/.test(fieldVal);
                break;
            case "username":
                result = /^[a-z]\w{3,}$/i.test(fieldVal);
                break;
            case "password":
                result = /^(?![a-zA-Z]+$)(?![a-zA-Z0-9]+$)(?![a-zA-Z\W_!@#$%^&*`~()-+=]+$)(?![a-zA-Z0-9]+$)(?![a-zA-Z\W_!@#$%^&*`~()-+=]+$)(?![0-9\W_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9\W_!@#$%^&*`~()-+=]{8,}$/.test(fieldVal);
                break;
            case "mobile":
                result = /^1(3|4|5|6|7|8|9)\d{9}$/.test(fieldVal);
                break;
            case "phone":
                result = /^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{4,7}(\-\d{1,4})?$/.test(fieldVal);
                break;
            case "tel":
                result = /^1(3|4|5|6|7|8|9)\d{9}$/.test(fieldVal) || /^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{4,7}(\-\d{1,4})?$/.test(fieldVal);
                break;
            case "email":
                result = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(fieldVal);
                break;
            case "idcard":
                result = /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[0-2])(0[1-9]|[012]\d|3[0-3])\d{3}(\d|X)$/.test(fieldVal);
                break;
        }

        return result;
    }

    /**
     * 获取数据值
     * @param data 数据
     * @param key 数据标识 支持二维
     * @returns {null}
     */
    _getDataValue(data, key) {
        let fieldVal = null;
        if (tool.isNumber(key)) {
            fieldVal = key;
        } else if (tool.isString(key) && /\./.test(key)) {
            // 支持多维数组验证
            let arr = key.split('.');
            for (let i = 0; i < arr.length; i++) {
                key = arr[i];
                if (!(key in data)) {
                    fieldVal = null;
                    break;
                }

                fieldVal = data = arr[key];
            }
        } else {
            fieldVal = key in data ? data[key] : null;
        }

        return fieldVal;
    }
}

module.exports = validate;